#include <vector>
#include <iostream>
#include <map>
#include <fstream>
#include "ElementXML.h"
#include "XML.h"
#include "NodeXML.h"
#include "AttributeXML.h"

int toHtml(NodeXML * node, map<string, NodeXML *> mapXslt, string fileName );
int ProcessNode(NodeXML * elemMAP, string fileName, NodeXML * node);

int write_file(string fileName, string content)
{
	//Ouverture du fichier 
	ofstream fichier (fileName.c_str(), ios::out | ios::ate);
	
	if (fichier.is_open())
	{
		//On écrit content dans le fichier
		fichier << content;
		return 0;
	}
	else
		return -1;
}

//Cette fonction initialise la map qui va permettre de traiter le document xml
int treeTransformation(const XML * xml, const XML * xslSheet, string fileName)
{
	//On crée un map
	map<string, NodeXML *> mapXsl;
	//On déclare un iterateur sur la liste des fils de l'element xml sxlsheet
	ElementXML * rootXsl = xslSheet->getRoot();
	vector<NodeXML *> * children = rootXsl->getChildren();
	vector<NodeXML *>::iterator it = children->begin();
	
	//on parcours les fils jusqua trouver un template du style : <xsl:template match="rapport">
	for ( ; it != children->end(); it++)
	{
		//Si on tombbe sur un template
		if ((*it)->getName() == "xsl:template") 
		{
			//On récupêre la valeur des attribut pout avoir le nom associé au template : dans l'exemple on cherche "rapport"
			vector<AttributeXML *> *  vectAtt = (*it)->getAttributes();
			//On insere d'abord dans la map le nom associé au template, ici "rapport", puis on insère l'Element NodeXml associé
			mapXsl.insert(pair<string, NodeXML *>((*vectAtt)[0]->getValue(), (*it)));
		}

	}
	
	//On récupere la racine de l'élèment xml
	NodeXML * node = xml-> getRoot();
	//On appelle la fonction toHtml qui va traiter les noeuds
	toHtml( node, mapXsl, fileName);
	 
}

//Cette fonction traite tous les fils du noeuds initial
int toHtml(NodeXML * node, map<string, NodeXML *> mapXslt, string fileName  )
{
	
	//We try to dynamic cast if node is a ElementXml we get the name
	// else if node is a TextXML we copy the node in the file
	//En gros, si l'objet est de type TextXML, c'est a dire que du blabla on le recopie directement dans le fichier html
	string nodeName = node->getName();
	if (nodeName == "<>")
	{
		//Node is a TextXml
		write_file(fileName, node->getContent());
		
	}

	//On déclare un iterateur sur la map
	map<string, NodeXML*>::iterator it;
	//On cherche le nom du noeud xml dans la map pour voir si un template lui est associé
	it = mapXslt.find(nodeName);	
	
	//If we find the template in the map
	if (it != mapXslt.end())
	{	
		//On a trouvé un template, on récupere l'objet xml associé au template pour connaitre le traitement a effectué	
		NodeXML * elemMAP = it->second;
		int value = ProcessNode(elemMAP, fileName, node);
		//If ProcessNode find the template apply-template we don't process the children
		//Si la fonction ProcessNode renvoie -1 c'est qu'il y a eu un apply-template donc que les fils ont déja été traité, on a pas a traité de nouveaux les fils
		if ( value == -1)
			return 1;
	
		
	}
	//On récupere les fils du noeuds courant
	vector<NodeXML *> * children = node->getChildren();
	vector<NodeXML *>::iterator _it = children->begin();
	
	for ( ; _it != children->end(); _it++)
	{
		//On rappelle la fonction pour chaque fils
		toHtml( node, mapXslt, fileName);
	}
	 return 0;
}

//C'est galere à partir d'ici
//On travaille à partir de l'élèment de la map, c'est a dire du XSLT
int ProcessNode(NodeXML * elemMAP, string fileName, NodeXML * node)
{
	//On récupère les fils de l'élèment et on déclare un iterateur
	vector<NodeXML *> * childNode = elemMAP->getChildren();
	vector<NodeXML *>::iterator it = childNode->begin();
	
	
	//Tant qu'il y a des fils ont regarde la valeur
	while (it != childNode->end())
	{
		if ((*it)->getName()  == "xsl:value-of")
		{
			//OPTIONNEL!!!!!!
			//ProcessValueOf();
		}
		else if ((*it)->getName()  == "xsl:apply-templates" )
		{
			//ProcessApplyTemplate
		}
		//On écrit la balise dans le fichier html
		string content = "<" + (*it)->getName() + ">" + '\n';
		write_file(fileName, content);
		//On rappelle la fonction avec les fils
		ProcessNode(*it, fileName, node);
	}
		
	//On écrit la fin de la balise dans le fichier
	string contentEnd = "<\\" + (*it)->getName() + ">" + '\n';
	write_file(fileName, contentEnd);

	return 0;

}

/**Remove the more externs <xsl:template match=""> tags from a string*/
void removeExternsTags(string s)
{
	//remove the opening tag
	s.erase(1, s.find_first_of(">"));
	
	size_t last = s.find_last_of(">");
	//remove the close tag
	s.erase(last - 15, last);
}

void ProcessApplyTemplate ()
{

}

/* une fonction a partir d'un template pour savoir si on a un template


//**************************************************/
/****************************************************/

/**Remove the more extern tags OK**/
string RemoveExternTags(string s)
{
	//remove the opening tag
	s.erase(0, s.find_first_of(">") + 1 );
	
	size_t last_closing = s.find_last_of(">");
        
        size_t last_opening = s.find_last_of("<");
        
	s.erase(last_opening, last_closing - last_opening + 1);
         
        return s;
}

string RemoveFirstTag(string s)
{
    //remove the opening tag
    size_t first_opening = s.find_first_of("<");
    size_t first_closing = s.find_first_of(">");
    s.erase(first_opening, first_closing - first_opening + 1 );

    return s;
}
/*Remove the text until the first tag( suppose that s begin with text)*/
string RemoveFirstText(string s)
{
   return s.erase(0, s.find_first_of("<"));
}


int CheckConformity(string str)/*Not ok*/
{
    size_t found;
    int nb;

    found = str.find_first_of("<>");
    while (found != string::npos)
    {
        nb++;
        found = str.find_first_of("<>",found++);
        cout << nb << endl;
    }
    
    return ((nb % 2 == 0) && nb > 0 ) ? nb : -1;
}


/**Check if the tag has to be added to the html file OK**/
bool addFirstTag(string s)
{
    if (s.substr(s.find_first_of("<"), s.find_first_of(">") + 1) == 
            "<xsl:apply-templates />" )
        cout << "gagné" << endl;
    else 
        cout << "perdu" << endl;
    return true;
}

/*returns the containt of the tag s OK*/
string BetweenTheFirstTag(string s)
{
    return(s.substr(s.find_first_of("<") + 1, s.find_first_of(">") -1));
}

/*Check if the balise is an xsl tag or not OK*/
bool IsAnXslTag(string s)
{
    string str = s.substr(s.find_first_of("<") + 1, s.find_first_of(">") -1);
    if (str.find("xsl:") != string::npos )//it's an xsl tag
        return true;
    else 
        return false;
}

/*Check if the string begin with a tag OK*/
bool BeginWithATag(string s)
{
    if (s.find('<', 0) != string::npos)
        return true;
    else 
        return false;
}

string GetFirstTag(string s)
{
    return(s.substr(s.find_first_of("<"), s.find_first_of(">") + 1));
}

string GetTextToNextTag(string s)
{
    return s.substr(0, s.find_first_of('<'));
}

/*This function returns the string to write in the html file*/
string AssociateNodeTemplate(NodeXML * node, NodeXML * _template)
{
	string s = _template->toString();
	string result;
	if (int nbTag = CheckConformity(s) == -1) 
	{
		cerr << "One or more characters '<' / '>' are in the string";
		return NULL; 
	}

	
	RemoveExternTags(s);

	while(s != "")
	{
            if (BeginWithATag(s))
            {
                string firstTag = GetFirstTag(s);    
                if (IsAnXslTag(firstTag))
                {
                	if (firstTag.find("xsl:template", 0) != string::npos)
			{
				cout << "xsl:template" << endl;
			}
			else if (firstTag.find("xsl:apply-templates", 0) != string::npos)
			{
				cout << "xsl:apply-templates" << endl;
			}
			else if (firstTag.find("xsl:value-of", 0) != string::npos)
			{
				cout << "xsl:value-of" << endl;
			}
                }
             
		else //Not an xsl tag
		{
			result += firstTag;
			cout << "Not an xsl tag" << endl;
		}	
                RemoveFirstTag(s);    

            }
            else //do not begin with a tag
            {
                
                //it is some text that we add to result
                result += GetTextToNextTag(s);
                RemoveFirstText(s);
            }
        }
}
